import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import 'styles/components/formula-input.scss'
import { EventUtils } from 'share/common'

class FormulaInput extends Component {

  state = {
    activated: false,
    pointerIndex: -1
  };

  mainDOM = null;
  PADDING = 20;
  LINE_HEIGHT = 20;

  componentDidMount() {
    this.mainDOM = ReactDOM.findDOMNode(this);
    const { cursor, onRef } = this.props;
    onRef && onRef(this);
    if(cursor) {
      EventUtils.addHandler(document, 'click', this.watchActivated);
      EventUtils.addHandler(document, 'keydown', this.onKeyDown);
    }
  }

  componentWillUnmount() {
    const { cursor } = this.props;
    if(cursor) {
      EventUtils.removeHandler(document, 'click', this.watchActivated);
      EventUtils.removeHandler(document, 'keydown', this.onKeyDown);
    }
  }

  //点是否在矩形范围内
  pointInRect(point, rect) {
    return (point.left >= rect.left && point.left <= rect.width + rect.left) &&
      (point.top >= rect.top && point.top <= rect.height + rect.top)
  }

  setPointerIndex = pointerIndex => {
    this.setState({ pointerIndex });
  };

  //根据点的位置计算索引数
  computePointerIndex = position => {
    let pointerIndex = -1;
    const factors = this.mainDOM.getElementsByClassName('factor');
    const mainDOMRect = this.mainDOM.getBoundingClientRect();
    let positionInFactor = false;
    //每一行结束的factor索引
    let factorRow = [];
    let rowTop;
    let factorLength = factors.length;
    Array.prototype.map.call(factors, (factor, i) => {
      const factorRect = factor.getBoundingClientRect();
      const factorOffsetRect = {
        left: factorRect.left - mainDOMRect.left,
        top: factorRect.top - mainDOMRect.top,
        width: factorRect.width,
        height: factorRect.height
      };
      //如果点在因子的矩形范围内，则有索引
      if(this.pointInRect(position, factorOffsetRect)) {
        positionInFactor = true;
        //前半部分为上一个，后半部分为当前
        pointerIndex = i + (((position.left - factorOffsetRect.left) < factorOffsetRect.width / 2) ? -1 : 0);
      }
      if(!rowTop) {
        rowTop = factorOffsetRect.top;
      }
      if(rowTop !== factorOffsetRect.top) {
        rowTop = factorOffsetRect.top;
        factorRow.push(i - 1);
      }
      if(i === factorLength - 1) {
        factorRow.push(i);
      }
    });
    let rows = factorRow.length;
    if(!positionInFactor && rows !== 0) {
      if(position.top < this.PADDING)
        pointerIndex = -1;
      else {
        //根据点的位置和行高计算行数
        let positionRow = Math.floor((position.top - this.PADDING) / this.LINE_HEIGHT);
        if(position.left < this.PADDING) {
          if(positionRow === 0)
            pointerIndex = -1;
          else if(positionRow > rows - 1)
            pointerIndex = factors.length - 1;
          else
            pointerIndex = factorRow[positionRow - 1];
        } else {
          pointerIndex = factorRow[positionRow] || (factors.length - 1);
        }
      }
    }
    return pointerIndex;
  };

  getOffsetPosition = e => {
    const mainDOMRect = this.mainDOM.getBoundingClientRect();
    return {
      left: e.clientX - mainDOMRect.left,
      top: e.clientY - mainDOMRect.top
    }
  };

  computePointerPosition = index => {
    if(index === -1 || !this.mainDOM) {
      return {
        left: this.PADDING,
        right: this.PADDING
      }
    } else {
      const factors = this.mainDOM.getElementsByClassName('factor');
      if(factors && factors[index]) {
        const factorRect = factors[index].getBoundingClientRect();
        const mainDOMRect = this.mainDOM.getBoundingClientRect();
        return {
          left: factorRect.left - mainDOMRect.left + factorRect.width,
          top: factorRect.top - mainDOMRect.top
        }
      } else {
        return {
          left: this.PADDING,
          top: this.PADDING
        }
      }
    }
  };

  addFactor(factor) {
    let { pointerIndex } = this.state;
    const { factors } = this.props;
    const result = [].concat(factors);
    result.splice(pointerIndex + 1, 0, factor);
    return [result, () => {
      setTimeout(() => {
        pointerIndex++;
        this.setState({ pointerIndex, activated: true });
      }, 0);
    }]
  }


  watchActivated = e => {
    this.setState({ activated: false }, () => {
      const target = EventUtils.getTarget(e);
      if(this.mainDOM.contains(target)) {
        let pointerIndex = this.computePointerIndex(this.getOffsetPosition(e));
        this.setState({ activated: true, pointerIndex });
      } else {
        this.setState({ activated: false });
      }
    });
  };

  deleteFactor = () => {
    let { activated, pointerIndex } = this.state;
    const { factors } = this.props;
    if(activated) {
      if(pointerIndex !== - 1) {
        return [pointerIndex, () => {
          setTimeout(() => {
            pointerIndex--;
            this.setState({ pointerIndex, activated: true });
          });
        }];
      }
    } else {
      if(factors.length > 0) {
        let index = factors.length - 1;
        return[index, () => {
          setTimeout(() => {
            pointerIndex = factors.length - 1;
            this.setState({ pointerIndex, activated: true });
          }, 0);
        }];
      }
    }
  };

  onKeyDown = e => {
    e.preventDefault();
    e.stopPropagation();
    let { activated, pointerIndex } = this.state;
    const { factors, onDelete } = this.props;
    const pointerPosition = this.computePointerPosition(pointerIndex);
    if(activated) {
      switch(e.keyCode) {
        //delete
        case 8:
          if(pointerIndex !== - 1) {
            onDelete(pointerIndex, () => {
              pointerIndex--;
              this.setPointerIndex(pointerIndex);
            });
          }
          break;
        //⬅
        case 37:
          pointerIndex !== -1 && pointerIndex--;
          this.setPointerIndex(pointerIndex);
          break;
        //⬆
        case 38:
          pointerIndex = this.computePointerIndex({
            left: pointerPosition.left,
            top: pointerPosition.top - this.LINE_HEIGHT / 2
          });
          this.setPointerIndex(pointerIndex);
          break;
        //➡
        case 39:
          pointerIndex !== factors.length - 1 && pointerIndex++;
          this.setPointerIndex(pointerIndex);
          break;
        //⬇
        case 40:
          pointerIndex = this.computePointerIndex({
            left: pointerPosition.left,
            top: pointerPosition.top + this.LINE_HEIGHT * 1.5
          });
          this.setPointerIndex(pointerIndex);
          break;
      }
    }
  };

  renderFactorItem = (factor, index) => {
    const { renderFactor } = this.props;
    const children = renderFactor(factor);
    if(typeof children !== 'string') {
      let className = children.props.className || '';
      className += `${className ? ' ' : ''}factor`;
      return React.cloneElement(
        children,
        {
          key: index,
          className
        }
      )
    } else {
      return <span className='factor' key={index}>
        {children}
      </span>
    }
  };

  render() {
    const { factors, cursor } = this.props;
    const { activated, pointerIndex } = this.state;
    const pointerPosition = cursor ? this.computePointerPosition(pointerIndex) : {};
    return (
      <div className="formula-input">
        {activated && <div className='formula-input-cursor' style={pointerPosition}/>}
        {factors.map(this.renderFactorItem)}
      </div>
    );
  }

  static propTypes = {
    factors: PropTypes.array,
    renderFactor: PropTypes.func,
    cursor: PropTypes.bool,
    onDelete: PropTypes.func
  };

  static defaultProps = {
    factors: [],
    cursor: true,
    renderFactor: text => text,
    onDelete: () => {}
  };

}


export default FormulaInput;
